#include "cheader.h"
#include <stdlib.h>
#include "tablefuncs.h"

using std::cout;
using std::cerr;
using std::string;

string variables;
string classprototypes;
string classes;
string prototypes;

extern string limit;

Name *stripLast(Name *name)
{
    Name *newName = (Name*)malloc(sizeof(struct Name));
    if (name == 0 || name->next == 0)
    {
        cerr << "Unexpected error" << std::endl;
        strcpy(newName->identifier, "!@#$%^&"); // will fail ... probably ;)
        return newName;
    }
    
    Name *newNamePointer = newName;
    bool first = true;
    while (name->next != 0)
    {
        if (!first)
        {
            newNamePointer->next = (Name*)malloc(sizeof(struct Name));
            newNamePointer = newNamePointer->next;
        } else first = false;
        strcpy(newNamePointer->identifier, name->identifier);
        name = name->next;
        newNamePointer->next = 0;
    }
    return newName;
}

bool isFieldOrMethod(SymbolTable *fullTable, SymbolTable *table)
{
    Name *possibleClassName = stripLast(table->name);
        
    while (fullTable != 0)
    {
        if (compareNames(fullTable->name, possibleClassName) == 0 && fullTable->symbolType == Class)
            return true;
        fullTable = fullTable->next;
    }
    
    return false;
}

Name *getClassOfWhichThisIsTheFieldOrTheMethod(SymbolTable *fullTable, SymbolTable *table)
{
    Name *possibleClassName = stripLast(table->name);
    
    while (fullTable != 0)
    {
        if (compareNames(fullTable->name, possibleClassName) == 0 && fullTable->symbolType == Class)
            return possibleClassName;
        fullTable = fullTable->next;
    }
    
    std::cerr << "Unexisting class: ";
    printName(possibleClassName);
    std::cerr << std::endl;
    return 0;
}

void printCHeader(SymbolTable *table)
{
    SymbolTable *fullTable = table;
    if (table == 0)
    {
        cerr << "Module does not export any names" << std::endl;
        return;
    }
    
    while (table != 0)
    {
        if (nameInLimit(table->name))
        {
            if (table->symbolType == Var)
            {
                if (isFieldOrMethod(fullTable, table))
                {
                    // Do nothing, this will be handled with the class
                } else {
                    variables += "extern ";
                    variables += getInternalFromName(fullTable, table->type);
                    variables += "* ";
                    variables += table->internal;
                    variables += ";\n";
                }
            }
            if (table->symbolType == Func)
            {
                prototypes += getInternalFromName(fullTable, table->type);
                prototypes += "* ";
                prototypes += table->internal;
                prototypes += "(";
                if (isFieldOrMethod(fullTable, table))
                {
                    prototypes += getInternalFromName(fullTable, getClassOfWhichThisIsTheFieldOrTheMethod(fullTable, table));
                    prototypes += "*";
                }
                
                NameList *nl = table->argumentTypes;
                bool first = true;
                while (nl != 0)
                {
                    if (first && isFieldOrMethod(fullTable, table))
                    {
                        prototypes += ","; first = false;
                    }
                    prototypes += getInternalFromName(fullTable, nl->name);
                    prototypes += "*";
                    if (nl->next != 0)
                    {
                        prototypes += ",";
                    }
                    nl = nl->next;
                }
                prototypes += ");\n";
            }
            
            if (table->symbolType == Class)
            {
                // prototype
                classprototypes += "typedef struct ";
                classprototypes += getInternalFromName(fullTable, table->name);
                classprototypes += "s ";
                classprototypes += getInternalFromName(fullTable, table->name);
                classprototypes += ";\n";
                
                // class
                classes += "struct ";
                classes += getInternalFromName(fullTable, table->name);
                classes += "s {\n";
                SymbolTable *table2 = fullTable;
                while (table2 != 0)
                {
                    if (table2->symbolType == Var && isFieldOrMethod(fullTable, table2))
                    {
                        if(compareNames(getClassOfWhichThisIsTheFieldOrTheMethod(fullTable, table2), table->name) == 0)
                        {
                            classes += getInternalFromName(fullTable, table2->type);
                            classes += "* ";
                            classes += table2->internal;
                            classes += ";\n";
                        }
                    }
                    table2 = table2->next;
                }
                classes += "};\n";
                
                // init and destroy
                prototypes += "void ";
                prototypes += getInternalFromName(fullTable, table->name);
                prototypes += "_init(";            
                prototypes += getInternalFromName(fullTable, table->name);
                prototypes += "*);\n";
                prototypes += "void ";
                prototypes += getInternalFromName(fullTable, table->name);
                prototypes += "_destroy(";
                prototypes += getInternalFromName(fullTable, table->name);
                prototypes += "*);\n";
            }
            if (table->symbolType == Constructor)
            {
                prototypes += getInternalFromName(fullTable, table->type);
                prototypes += "* ";
                prototypes += table->internal;
                prototypes += "(";
                            
                NameList *nl = table->argumentTypes;
                while (nl != 0)
                {
                    prototypes += getInternalFromName(fullTable, nl->name);
                    prototypes += "*";
                    if (nl->next != 0)
                    {
                        prototypes += ",";
                    }
                    nl = nl->next;
                }
                prototypes += ");\n";
            }
        }
        table = table->next;
    }
    std::cout << classprototypes;
    std::cout << classes;
    std::cout << variables;
    std::cout << prototypes;
}

